﻿//For public functions (used by manage page)
var feedback = {};

//The following variable are used when feedback is running within a local prototype
var authCookieValue = null;

//Content URL path is relative if prototype is hosted on Axshare...
var contentUrlPrefix = '';

// to isolate the scope of this plugin
; (function () {
    var localSetContinueAsAnon = false;
    var localCommenterName = null;

    //The following two variables are used when feedback is running within a local prototype
    //var authCookieValue = null;

    //For read only mode (when performing maintenance)
    var readOnlyMode = false;
    var readOnlyMessage = '';

    var feedbackServiceUrl = (window.AXSHARE_HOST_SECURE_URL || 'https://share.axure.com') + '/issue';

    //Else if a local prototype, make content path absolute.
    if ($axure.player.settings.isAxshare === false) {
        contentUrlPrefix = window.AXSHARE_HOST_URL || 'http://share.axure.com';
    }

    var getCookieValue = function (cookieName, decodeUri) {
        var nameEq = cookieName + '=';
        var retVal = null;
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEq) == 0) {
                retVal = c.substring(nameEq.length, c.length);
            }
        }

        if (!!retVal && decodeUri == true) {
            return decodeURIComponent(retVal);
        }

        return retVal;
    };

    $(document).ready(function () {
        //Following axure.utils function doesn't work properly in IE9
        //$axure.utils.loadCSS('/Content/plugins/feedback/styles/feedback.css');
        $('<link>').appendTo('head').attr({
            rel: 'stylesheet',
            type: 'text/css',
            href: contentUrlPrefix + '/Content/plugins/feedback/styles/feedback.css'
        });

        if (window.AXSHARE_ON_PREM) {
            $('<link>').appendTo('head').attr({
                rel: 'stylesheet',
                type: 'text/css',
                href: (window.location.protocol == 'http' ? 'http:' : 'https:') + '//www.axure.com/content/nexa_lightregular.css'
            });
        }

        //If the axDialog plugin isn't loaded yet, load it (used when loading within a prototype)
        if (!$.fn.axDialog) {
            $axure.utils.loadJS(contentUrlPrefix + '/Scripts/jquery.axDialog.js');
        }

        //If running within a prototype, check if feedback is enabled and load the plugin if so.
        if ($axure.player.prototype) {
            $axure.utils.loadJS(contentUrlPrefix + '/Scripts/jquery.inputHintOverlay.js');
            $axure.utils.loadJS(contentUrlPrefix + '/Scripts/twitlimit-0.2.0.compressed.js');
            $axure.utils.loadJS(contentUrlPrefix + '/Scripts/plugins/feedback/axImgAreaSelect.js');

            $.ajax({
                type: 'GET',
                url: feedbackServiceUrl + '/fbenabled',
                data: {
                    shortcut: $axure.player.settings.projectId
                },
                success: function (response) {
                    //If the feedback is enabled for this prototype...
                    if (response.success && response.data.feedbackEnabled == true) {
                        readOnlyMode = response.readOnlyMode;
                        readOnlyMessage = response.readOnlyMessage;

                        //If this is not an AxShare hosted prototype, authenticate the user and store the authCookie token, then load the plugin
                        //Else immediately load the plugin
                        if ($axure.player.settings.isAxshare === false) {
                            if (response.data.passwordProtected == true) {
                                getAuthTokenAndLoad();
                            } else {
                                $.ajax({
                                    type: 'GET',
                                    url: feedbackServiceUrl + '/Authenticate',
                                    data: {
                                        shortcut: $axure.player.settings.projectId,
                                        password: ''
                                    },
                                    success: function (response) {
                                        if (response.success == true) {
                                            authCookieValue = response.authToken;
                                            loadPlugin();
                                            clearAndLoadEntries(false);
                                        }
                                    },
                                    dataType: 'jsonp'
                                });
                            }
                        } else {
                            //For I.E., now we use authCookieValue all the time
                            //since it doesn't allow 3rd party cookies by default
                            //Checks for prototype token first, then user token
                            var tokenString = getCookieValue(response.data.viewCookieName, true);
                            if (tokenString == null) tokenString = getCookieValue(response.data.userCookieName, true);
                            if (tokenString != null) authCookieValue = tokenString;

                            loadPlugin();
                            clearAndLoadEntries(false);
                        }

                        // Bug (resolved): Screwy IE8 Issues. Doesn't load up everything, so can't run. Should just restrict to not IE8
                        if (!isIE8){
                            // If fragment contains certain variables, redirect to feedback 
                            showIssueOrComment();
                        }
                    }
                },
                dataType: 'jsonp'
            });
        }
    });

    //For local prototypes...
    //Gets the authtoken string from the server and stores it locally. If auth cookie is necessary and not present, then
    //will prompt the user for the prototype password or allows to continue without feedback. 
    //Once the token is successfully retrieved, feedback is loaded.
    var getAuthTokenAndLoad = function () {
        var listHtml = [];
        listHtml.push(
            '<div class="feedbackAuthDialog">',
            '    <form class="feedbackAuthForm" action="">',
            '        <center>',
            '            This prototype requires a password to enable discussions.<br/><br/>',
            '            Please enter the password or choose to continue without discussions.<br/><br/>',
            '            <div style="display:none; color: red;" id="invalidPass">Incorrect Password,<br/>Please Try Again<br/></div>',
            '            <div style="width: 100%;">',
            '                <div style="height: 100%; width: 71%; float: left;">',
            '                    <input style="float: right; width: 120px; margin-top: 1px;" type="password" title="Enter Password" />',
            '                </div>',
            '                <div style="height: 100%; width: 27%; float: right;">',
            '                    <input style="float: left;" type="submit" value="OK" />',
            '                </div>',
            '            </div>',
            '            <br/><br/>',
            '            <a href="" class="continueNoFeedback">Continue without discussions</a>',
            '        </center>',
            '    </form>',
            '</div>');

        $('#interfaceControlFrame').append(listHtml.join(''));

        $('.feedbackAuthDialog').axDialog();

        $('.continueNoFeedback').click(function () {
            var $parentDialogDiv = $(this).parents('.feedbackAuthDialog');
            $parentDialogDiv.axDialog('hide');

            return false;
        });

        var showFeedbackAuthDialog = function () {
            $('.feedbackAuthDialog input[type="password"]').val('');
            $('.feedbackAuthDialog').axDialog('show');
            $('.feedbackAuthDialog input[type="password"]').focus();
        };

        //Attempts to get the auth token
        //If success, store the token and load the feedback plugin
        //If fail, show the auth dialog
        var getAuth = function () {
            $.ajax({
                type: 'GET',
                url: feedbackServiceUrl + '/GetAuthToken',
                data: {
                    shortcut: $axure.player.settings.projectId
                },
                success: function (response) {
                    if (!response.success && response.redirecturl) {
                        showFeedbackAuthDialog();
                        return;
                    }

                    if (response.success) {
                        authCookieValue = response.authToken;
                        loadPlugin();
                        clearAndLoadEntries(false);
                    }
                },
                dataType: 'jsonp'
            });
        };

        //Executes when the user submits the prototype password
        //If the password is wrong, the auth prompt is redisplayed with an error message
        //If it is correct, then getAuth is executed again to retrieve the token
        $('.feedbackAuthForm').submit(function () {
            var feedbackPass = $(this).find('input[type="password"]').val();

            var $parentDialogDiv = $(this).parents('.feedbackAuthDialog');
            $parentDialogDiv.axDialog('hide');

            $.ajax({
                type: 'GET',
                url: feedbackServiceUrl + '/Authenticate',
                data: {
                    shortcut: $axure.player.settings.projectId,
                    password: feedbackPass
                },
                success: function (response) {
                    if (!response.success) {
                        $parentDialogDiv.find('#invalidPass').show();
                        showFeedbackAuthDialog();
                    } else {
                        //getAuth(); FOR NOW WE AREN'T USING getAuth() (PROB IN IE)
                        authCookieValue = response.authToken;
                        loadPlugin();
                        clearAndLoadEntries(false);
                    }
                },
                dataType: 'jsonp'
            });

            return false;
        });

        //Initial attempt to retrieve the auth token...
        //getAuth();
        //FOR NOW ALWAYS PROMPT! (PROB WITH LOCAL COOKIES IN IE)
        showFeedbackAuthDialog();
    };

    var loadPlugin = function () {
        // add the plugin to the interface plugin area
        $axure.player.createPluginHost({
            id: 'feedbackContainer',
            context: 'interface',
            title: 'Discuss'
        });

        // load when feedback loads
        $axure.page.bind('load.feedback', function () {
            clearAndLoadEntries(false);
        });

        $('#feedbackContainer').append('<div id="feedbackMinWidth"></div>');
        $('#feedbackMinWidth').append('<div id="refreshFeedbackDiv"><a id="refreshFeedbackLink" href="#"><img src="' + contentUrlPrefix + '/images/refresh.png" alt="Refresh" title="Refresh" /></a><img id="feedbackLoadingImg" src="' + contentUrlPrefix + '/images/fbloading.gif" alt="Refreshing..." title="Refreshing..." /></div>');
        $('#feedbackMinWidth').append('<div id="feedbackPageNameHeader">Discuss</div>');

        $('#refreshFeedbackLink').click(function () {
            clearAndLoadEntries(true);

            return false;
        });

        setupCommenterNameFields($('#feedbackMinWidth'));

        $('<div id="feedbackNewIssue" /><div id="feedbackSavedIssues" />').appendTo('#feedbackMinWidth');

        // Load Extension if it exists for fileURLs only!
        if (window.location.origin === "file://" || window.location.origin == "null") {
            window.postMessage('yesIAmAxure', '*');
        } 
    };

    var toggleRefreshLinkAndImage = function (isLoading) {
        if (isLoading) {
            $('#refreshFeedbackLink').hide();
            $('#feedbackLoadingImg').show();
        } else {
            $('#refreshFeedbackLink').show();
            $('#feedbackLoadingImg').hide();
        }
    };

    //------------------------------------------------------------------------------------
    //-----------BEGINNING OF CODE SECTION FOR 'COMMENTING AS' FUNCTIONALITY--------------
    //------------------------------------------------------------------------------------
    //Sets the commenter name cookie and updates all commenterName fields on the page
    var setCommenterName = function (value, days) {
        //If empty string is given as name, delete the cookie
        if (value.length == 0 || value.toUpperCase() == 'ANONYMOUS') {
            days = -1;
        }

        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            var expires = '; expires=' + date.toGMTString();
        }
        else var expires = '';
        document.cookie = 'feedbackCommenterName=' + value + expires + '; path=/';
        localCommenterName = value;

        if (days < 0) {
            $('.commenterName').text('Anonymous');
            localCommenterName = null;
        } else {
            $('.commenterName').text(value);
        }
    };

    //Gets the commenter name from the cookie or returns 'Anonymous' if one doesn't exist
    var getCommenterName = function () {
        if (localCommenterName != null) return localCommenterName;

        var cookieVal = getCookieValue('feedbackCommenterName');

        //Return the cookie val else return 'Anonymous' if the cookie doesn't exist
        return cookieVal == null ? 'Anonymous' : cookieVal;
    };

    //Set up and initialization of commentername display at the top of the feedback container and
    //the popup rename div, including generating the html and binding events to it
    var setupCommenterNameFields = function ($parentContainer) {
        var listHtml = [];
        listHtml.push(
            '<div id="commenterRenameContainer">',
            '    Commenting as <a class="commenterRenameLink" href=""><span class="commenterName">', getCommenterName(), '</span></a>',
            '    <div style="display:inline;" class="commenterRename">',
            '        <form class="commenterRenameForm" style="display:inline;" action="">',
            '            <input type="text" />',
            '            <input type="submit" value="OK" />',
            '            <input class="commenterRenameCancel" type="button" value="Cancel" />',
            '        </form>',
            '    </div>',
            '</div>');

        $parentContainer.append(listHtml.join(''));
        $('.commenterRename').hide();

        listHtml = [];
        listHtml.push(
            '<div class="commenterSetNameDialog">',
            '    <form class="commenterRenameForm" action="">',
            '        <center>',
            '            You are currently commenting anonymously.<br/><br/>',
            '            Please enter a name or click Continue as Anonymous<br/><br/>',
            '            <div style="width: 100%;">',
            '                <div style="height: 100%; width: 71%; float: left;">',
            '                    <input style="float: right; width: 120px; margin-top: 1px;" type="text" title="Enter Name" />',
            '                </div>',
            '                <div style="height: 100%; width: 27%; float: right;">',
            '                    <input style="float: left;" type="submit" value="OK" />',
            '                </div>',
            '            </div>',
            '            <br/><br/>',
            '            <a href="" class="commenterSetNameContinueAnon">Continue as Anonymous</a>',
            '        </center>',
            '    </form>',
            '</div>');

        $('#feedbackMinWidth').append(listHtml.join(''));

        $('.commenterSetNameDialog').axDialog();

        $('.commenterRenameLink').click(function () {
            $('.commenterRename input[type="text"]').val($(this).text());
            $(this).hide();
            $('.commenterRename').show();
            $('.commenterRename input[type="text"]').select();
            return false;
        });

        $('.commenterRenameCancel').click(function () {
            $('.commenterRename').hide();
            $('.commenterRenameLink').show();

            return false;
        });

        $('.commenterSetNameContinueAnon').click(function () {
            var $parentDialogDiv = $(this).parents('.commenterSetNameDialog');
            $parentDialogDiv.axDialog('hide');

            return false;
        });

        $('.commenterRenameForm').submit(function () {
            var commenterName = $.trim($(this).find('input[type="text"]').val());
            setCommenterName(commenterName, 7);

            if ($(this).parent('.commenterSetNameDialog').length > 0) {
                var $parentDialogDiv = $(this).parents('.commenterSetNameDialog');
                $parentDialogDiv.axDialog('hide');
            } else {
                $('.commenterRename').hide();
                $('.commenterRenameLink').show();
            }

            return false;
        });
    };

    //If the commenter name isn't set and the user hasn't been prompted already, this function
    //displays the Commenter Name dialog box and then triggers the given completeFunction with the given arg
    //If no dialog box is shown, the completeFunction is immediately triggered with the given arg
    var showCommenterNameDialogDiv = function (completeFunction, completeFunctionArg) {
        if (getCommenterName() == 'Anonymous' && !skipAnonNameCheck() && $axure.player.prototype) {
            $('.commenterSetNameContinueAnon').bind('click.commenterDialog', function (event) {
                document.cookie = 'skipAnonymousCheck=true';
                localSetContinueAsAnon = true;

                completeFunction(completeFunctionArg);
                $(this).unbind(event);

                //Unbind the submit event from the commenterRenameForm
                //that was bound in the next code block below
                //(Prevents it from triggering when the inline form is used)
                $(this).parents('.commenterRenameForm').unbind('.commenterDialog');
            });

            $('.commenterSetNameDialog .commenterRenameForm').bind('submit.commenterDialog', function (event) {
                document.cookie = 'skipAnonymousCheck=true';

                completeFunction(completeFunctionArg);
                $(this).unbind(event);

                //Unbind the click event from the 'continue as anonymouse' link
                //that was bound in code block above
                //(Prevents duplicate bindings since it is bound everytime the dialog is displayed)
                $(this).find('.commenterSetNameContinueAnon').unbind('.commenterDialog');
            });

            $('.commenterSetNameDialog input[type="text"]').val('');
            $('.commenterSetNameDialog').axDialog('show');

            $('.commenterSetNameDialog input[type="text"]').focus();


            // Re-locate dialog box and overlay
            setTimeout(function () {
                $('#commenterSetNameDialogOverlay').css('width', $('#feedbackContainer').width() + 'px');
                $('#commenterSetNameDialogOverlay').css('height', ($('#feedbackContainer').height() - 20) + 'px');
                $('.commenterSetNameDialog').css('top', parseInt($('.commenterSetNameDialog').css('top')) / 2 + 'px');

                var left = ($('#commenterSetNameDialogOverlay').width() - $('.commenterSetNameDialog').width()) / 4;
                $('.commenterSetNameDialog').css('left', left + 'px');
            }, 50);
        } else {
            completeFunction(completeFunctionArg);
        }
    };

    //Returns true if the anonymous-name-check cookie has been set, so that user isn't prompted
    //again for a commenting name
    var skipAnonNameCheck = function () {
        // skip the cookie check if they've chose to continue as anon
        if (localSetContinueAsAnon) return true;

        var cookieValue = getCookieValue('skipAnonymousCheck');

        if (cookieValue == 'true') {
            return true;
        } else {
            return false;
        }
    };
    //------------------------------------------------------------------------------------
    //---------------END OF CODE SECTION FOR 'COMMENTING AS' FUNCTIONALITY----------------
    //------------------------------------------------------------------------------------


    //------------------------------------------------------------------------------------
    // This loads the entries for the current page in the plugin.
    // The callback of this function is the listCompleted function which processes the result
    //------------------------------------------------------------------------------------
    var clearAndLoadEntries = function (isRefresh) {
        var savedIssuesDiv = $('#feedbackSavedIssues');

        if (!isRefresh) {
            var pageName = '';
            if ($axure.page.pageName) pageName = $axure.page.pageName;
            $('#feedbackPageNameHeader').html('Discuss ' + pageName);
            $('#feedbackPageNameHeader').attr('title', 'Discuss ' + pageName);

            $('#feedbackNewIssue').html('').append(getNewIssueControl());

            // New Topic focus event
            $('#addTopicInput').focus(function() {
                $currFeedbackEntry_keyboard = $(this);
            });

            savedIssuesDiv.removeClass('loading');
            toggleRefreshLinkAndImage(false);
        }

        if (savedIssuesDiv.is('.loading')) {
            return;
        } else {
            savedIssuesDiv.addClass('loading');
            toggleRefreshLinkAndImage(true);
        }

        var ajaxData = {
            shortcut: $axure.player.settings.projectId,
            pageId: $axure.page.id
        };

        //If the authCookieValue is set (a password-protected local prototype), then send the
        //token as well (because cookies don't always get sent to external domains)
        if (authCookieValue) {
            $.extend(ajaxData, { token: authCookieValue });
        }

        savedIssuesDiv.html('');
        $.ajax({
            type: 'GET',
            url: feedbackServiceUrl + '/list',
            data: ajaxData,
            success: function (response) {
                if (!response.success && response.redirecturl) {
                    window.location.href = response.redirecturl;
                    return;
                }

                var sortedIssues = fixIssueDatesAndSort(response.data.issues);
                var issueQuery = getIssuesControl(sortedIssues, false);
                savedIssuesDiv.append(issueQuery);

                // Screwy IE 8 Bug: Sometimes for IE8, items aren't registered as loaded yet so it freaks out.
                if (isIE8) {
                    (function wait() {
                        if (window.loaded) {
                            // Displays message to notify users that screenshot functionality is on another browser
                            checkIfShowScreenshotBrowserMsg();

                            // Screenshot Preloading Complete Callbacks
                            issueQuery.find('.thumbnailImg').each(function() {
                                $(this).load(function() {
                                    screenshotFinishLoadingCallback($(this).parent().parent());
                                    screenshotBindToDiv($(this).parent().parent());
                                });
                            });
                        } else {
                            setTimeout(wait, 5);
                        }
                    })();
                } else {
                    // Screenshot Preloading Complete Callbacks
                    issueQuery.find('.thumbnailImg').each(function() {
                        $(this).load(function() {
                            screenshotFinishLoadingCallback($(this).parent().parent());
                            screenshotBindToDiv($(this).parent().parent());
                        });
                    });
                }

                savedIssuesDiv.removeClass('loading');
                toggleRefreshLinkAndImage(false);
            },
            dataType: 'jsonp'
        });
    };

    // The following 3 functions are used by the issue management page
    //------------------------------------------------------------------------------------
    // This function creates an issue jQuery object from a given issue json object
    // It is used when an issue is displayed in a dialog on the issue management page
    //------------------------------------------------------------------------------------
    var managePageDeleteCallbackFunc = null;
    feedback.getWholeIssueQuery = function (issueJson, deleteIssueCallbackFunc) {
        managePageDeleteCallbackFunc = deleteIssueCallbackFunc;

        var sortedIssue = fixIssueDatesAndSort([issueJson]);
        var issueQuery = getIssuesControl(sortedIssue, true);

        // Screenshot Preloading Complete Callbacks
        issueQuery.find('.thumbnailImg').each(function () {
            $(this).load(function () {
                screenshotFinishLoadingCallback($(this).parent().parent());
                screenshotBindToDiv($(this).parent().parent());
            });
        });

        return issueQuery;
    };
    feedback.setCommenterName = function (value, days) {
        setCommenterName(value, days);
    };
    feedback.getCommenterName = function () {
        return getCommenterName();
    };
    feedback.setReadOnlyModeAndMessage = function (roMode, roMessage) {
        readOnlyMode = roMode;
        readOnlyMessage = roMessage;
    };

    //------------------------------------------------------------------------------------
    // This is utilized in several of the functions below, returns true if the given
    // element is an array.
    //------------------------------------------------------------------------------------
    var isArray = function (testObject) {
        return testObject && !(testObject.propertyIsEnumerable('length')) && typeof testObject === 'object' && typeof testObject.length === 'number';
    };

    //------------------------------------------------------------------------------------
    // Fixes issue date fields and returns the issues sorted by modified date or created date
    //  if it hasn't yet been modified.
    //------------------------------------------------------------------------------------
    var fixIssueDatesAndSort = function (issues, sortFn) {
        var items = issues;
        if (!isArray(issues)) {
            items = [issues];
        }

        $.each(items, function (index, item) {
            var dateStr = item.createdDate;
            item.createdDate = new Date(Number(dateStr.substring(6, dateStr.length - 2)));

            dateStr = item.modifiedDate;
            if (dateStr != null) {
                item.modifiedDate = new Date(Number(dateStr.substring(6, dateStr.length - 2)));
            }
        });

        if (!sortFn) {
            sortFn = function (x, y) {
                var a = x.modifiedDate ? x.modifiedDate : x.createdDate;
                var b = y.modifiedDate ? y.modifiedDate : y.createdDate;

                if (a === b) return 0;
                else if (a < b) return 1;
                else return -1;
            };
        }
        items.sort(sortFn);
        return items;
    };

    //------------------------------------------------------------------------------------
    // Fixes comment date fields and
    //      Returns an array of the comments sorted by created date
    //------------------------------------------------------------------------------------
    var fixCommentDatesAndSort = function (comments, sortFn) {
        var items = comments;
        if (!isArray(comments)) {
            items = [comments];
        }

        $.each(items, function (index, item) {
            var dateStr = item.commentDate;
            item.commentDate = new Date(Number(dateStr.substring(6, dateStr.length - 2)));

            dateStr = item.commentModifiedDate;
            if (dateStr != null) {
                item.commentModifiedDate = new Date(Number(dateStr.substring(6, dateStr.length - 2)));
            }
        });

        if (!sortFn) {
            sortFn = function (x, y) {
                var a = x.commentDate;
                var b = y.commentDate;

                if (a === b) return 0;
                else if (a < b) return -1;
                else return 1;
            };
        }
        items.sort(sortFn);
        return items;
    };

    //------------------------------------------------------------------------------------
    // Creates the html for issues, creates a jquery object for them, and attaches the
    // necessary events.
    //------------------------------------------------------------------------------------
    var getIssuesControl = function (issues, isManagePage) {
        var dom = $(getHtmlForIssues(issues, isManagePage));

        dom.find('a.deleteIssueLink').click(deleteIssueLink_click);

        if (!isManagePage) {
            //For expand/collapse functionality
            dom.find('a.collapseIssueLink').hide();
            dom.find('.issueComments').hide();
            dom.find('a.expandIssueLink').click(expandIssueLink_click);
            dom.find('a.collapseIssueLink').click(collapseIssueLink_click);
        } else {
            dom.filter('.feedbackIssue').each(function (index, fbIssue) {
                var $commentsDiv = $(fbIssue).find('.issueComments');
                var issueCode = $(fbIssue).attr('id').replace('issue_', '');

                loadFeedbackForIssue($commentsDiv, issueCode);
            });
        }

        return dom;
    };

    //------------------------------------------------------------------------------------
    // Returns the html for single issue or a list of issues
    //------------------------------------------------------------------------------------
    var getHtmlForIssues = function (issues, isManagePage) {
        var items = issues;
        if (!isArray(issues)) {
            items = [issues];
        }

        var listHtml = [];
        $.each(items, function (index, issue) {
            listHtml.push(
                '<div class="feedbackIssue" id="issue_', issue.code, '">',
                    '<a class="deleteIssueLink" href="#">x</a>',
                    '<div class="topicHeader">', issue.description, '</div>',
            //                '   <h4>Issue Code: ', issue.code,
                    '<div class="topicSubHeader">started by <span class="topicCreator">', issue.creator, '</span>',
                    ' ', $axure.utils.formatDate(issue.createdDate), '</div>'
            //                    '<br/>Modified: ', issue.modifiedDate ? issue.modifiedDate.toLocaleString() : 'NONE',                    
                );

            if (issue.screenshotIds != null && issue.screenshotIds.length > 0) {
                for (var i = 0; i < issue.screenshotIds.length; ++i) {
                    listHtml.push(
                '   <div class="feedbackItem_thumbnail" id="screenshot_', issue.screenshotIds[i], '">',
                '       <div class="feedbackItem_thumbnailImg">',
                '           <img class="thumbnailImg" src="', issue.screenshotUrls[i], '" style="maxWidth: 90%; maxHeight: 130px; display: block; margin: 0 auto;" />',
                '           <img class="fullsizeImg" src="', issue.fullsizeUrls[i], '" />',
                '       </div>',
                '       <div class="preloadDiv">',
                '       </div>',
                '   </div>');
                }
            }

            if (!isManagePage) {
                listHtml.push(
                '   <div class="topicExpandContainer">',
                '   <a class="expandIssueLink">', (issue.numComments > 0 ? 'view' : 'add'), ' comments</a>',
                '   <a class="collapseIssueLink">hide comments</a></div>');
            }

            listHtml.push(
                '   <div class="issueComments"></div>',
                '</div>');
        });

        var html = listHtml.join('');
        return html;
    };

    //------------------------------------------------------------------------------------
    // Returns jQuery for the edit issue links. Generates the HTML and binds the click 
    // event
    //------------------------------------------------------------------------------------
    var getEditIssueLinksControl = function () {
        var dom = $('<div class="editIssueLinks"><a class="awesomeButton feedbackIssueNewCommentLink">add comment</a></div>');
        dom.find('a.feedbackIssueNewCommentLink').click(issueNewCommentLink_click);

        return dom;
    };

    //------------------------------------------------------------------------------------
    //---------THE FOLLOWING 3 FUNCTIONS HANDLE THE EXPANDING/COLLAPSING OF ISSUES--------
    //------------------------------------------------------------------------------------
    var expandIssueLink_click = function () {
        var $parentFeedbackIssue = $(this).parents('.feedbackIssue');
        var $commentsDiv = $parentFeedbackIssue.find('.issueComments');

        var issueCode = $parentFeedbackIssue.attr('id').replace('issue_', '');

        var jumpToAddBox = false;
        if ($(this).text().indexOf('add') != -1) {
            jumpToAddBox = true;
        }

        loadFeedbackForIssue($commentsDiv, issueCode, jumpToAddBox);

        $commentsDiv.show();

        $parentFeedbackIssue.find('.collapseIssueLink').show();
        $(this).hide();

        return false;
    };

    var collapseIssueLink_click = function () {
        var $parentFeedbackIssue = $(this).parents('.feedbackIssue');
        var $commentsDiv = $parentFeedbackIssue.find('.issueComments');

        $commentsDiv.hide();

        $parentFeedbackIssue.find('.expandIssueLink').show();
        $(this).hide();

        return false;
    };

    var loadFeedbackForIssue = function (commentsDivQuery, issuecode, jumpToAddBox) {
        if (commentsDivQuery.is('.loading')) {
            return;
        } else {
            commentsDivQuery.addClass('loading');
        }

        commentsDivQuery.empty();
        commentsDivQuery.append('<img src="' + contentUrlPrefix + '/Content/ajaxload.gif" alt="Loading...">');

        var ajaxData = {
            issueCode: issuecode,
            shortcut: $axure.player.settings.projectId
        };

        //If the authCookieValue is set (a password-protected local prototype), then send the
        //token as well (because cookies don't always get sent to external domains)
        if (authCookieValue) {
            $.extend(ajaxData, { token: authCookieValue });
        }

        $.ajax({
            type: 'GET',
            url: feedbackServiceUrl + '/getcommentsbyissuecode',
            data: ajaxData,
            success: function (response) {
                if (!response.success && response.redirecturl) {
                    window.location.href = response.redirecturl;
                    return;
                }

                var comments = fixCommentDatesAndSort(response.data.comments);
                var commentQuery = getCommentsControl(comments);

                if (comments.length == 0) {
                    commentQuery = $('<div class="noCommentsMessage">no comments</div>');
                }

                //This prevents the comments from being appended to the div if the
                //user quickly hit the "collapse" button after hitting expand (therefore expand visible again)
                if (commentsDivQuery.is(":visible")) {
                    commentsDivQuery.empty();
                    commentsDivQuery.append(commentQuery);
                    commentsDivQuery.append(getEditIssueLinksControl());

                    // Moves Delete Icon to correct spot because image is transformed due to css and is not visible until
                    // it is fully loaded
                    commentsDivQuery.find('.feedbackItem_deleteDiv').each(function () {
                        $(this).hide();
                    });

                    commentsDivQuery.find('.thumbnailImg').each(function () {
                        $(this).load(function () {
                            screenshotFinishLoadingCallback($(this).parent().parent());
                            screenshotBindToDiv($(this).parent().parent());
                        });
                    });

                    if (jumpToAddBox == true) {
                        commentsDivQuery.find('a.feedbackIssueNewCommentLink').click();
                    }

                    //If this issue is being displayed within a dialog (outside of a prototype), then update
                    //the dialog to recenter it
                    if (commentsDivQuery.parents('#issueDisplay').length > 0) {
                        commentsDivQuery.parents('#issueDisplay').axDialog('update');
                    }
                }

                commentsDivQuery.removeClass('loading');
            },
            dataType: 'jsonp'
        });
    };
    //------------------------------------------------------------------------------------
    //----------------END OF CODE FOR THE EXPANDING/COLLAPSING OF ISSUES------------------
    //------------------------------------------------------------------------------------

    //------------------------------------------------------------------------------------
    // Generates the html for a single comment or list of comments.
    //------------------------------------------------------------------------------------
    var getHtmlForComments = function (comments) {
        var items = comments;
        if (!isArray(comments)) {
            items = [comments];
        }

        var listHtml = [];
        $.each(items, function (index, item) {
            listHtml.push(
                '<div class="feedbackItem" id="comment_', item.id, '">',
                '   <div class="feedbackItem_header">',
                '      <div class="feedbackItem_commenter">', item.commenter, '</div>',
                '      <div class="feedbackItem_commentDate">', $axure.utils.formatDate(item.commentDate), '</div>',
                '   </div>',
                '   <div class="feedbackItem_commentText">', item.commentText.replace(/\n/g, "<br/>"), '</div>');
            if (item.commentModifiedDate != null) {
                listHtml.push(
                '   <div class="feedbackItem_commentLastModified">edited ', $axure.utils.formatDate(item.commentModifiedDate), '</div>');
            }
            listHtml.push(
                '   <div class="feedbackItem_links">',
                '       <a class="feedbackItemEditLink">edit</a><a class="feedbackItemDeleteLink">delete</a>',
                '   </div>');
            if (item.screenshotIds != null && item.screenshotIds.length > 0) {
                for (var i = 0; i < item.screenshotIds.length; ++i) {
                    listHtml.push(
                '   <div class="feedbackItem_thumbnail" id="screenshot_', item.screenshotIds[i], '">',
                '       <div class="feedbackItem_thumbnailImg">',
                '           <img class="thumbnailImg" src="', item.screenshotUrls[i], '" style="maxWidth: 90%; maxHeight: 130px; display: block; margin: 0 auto;" />',
                '           <img class="fullsizeImg" src="', item.fullsizeUrls[i], '" />',
                '       </div>',
                '       <div class="preloadDiv">',
                '       </div>',
                '   </div>');
                }
            }
            listHtml.push(
                '</div>');
        });
        var html = listHtml.join('');
        return html;
    };

    //------------------------------------------------------------------------------------
    // Generates the html for a list of comments (or a single comment), creates a jquery object
    // and attaches the necessary events. The comments should all belong to the same issue
    // and will be created without the div for the surrounding issue
    //------------------------------------------------------------------------------------
    var getCommentsControl = function (comments) {
        var returnVal = $(getHtmlForComments(comments));
        attachCommentControlEvents(returnVal);
        return returnVal;
    };

    //------------------------------------------------------------------------------------
    // Takes a jquery object with a bunch of comment nodes in it and attaches the necessary
    // events.  This is usually used by getCommentsControl
    //------------------------------------------------------------------------------------    
    var attachCommentControlEvents = function (query) {
        query.find('a.feedbackItemEditLink').click(
            editCommentLink_click);
        query.find('a.feedbackItemDeleteLink').click(
            deleteCommentLink_click);
    };

    //------------------------------------------------------------------------------------
    // Following 2 functions create the New Issue Entry section at the top of the feedback
    // container
    //------------------------------------------------------------------------------------
    var getNewIssueControl = function () {
        // add the area for a new issue entry box
        var newIssueQuery = $(getNewIssueHtml());
        // find the submit and cancel links and attach the click events
        newIssueQuery.find('.feedbackEntryItemSave').click(feedbackNewIssueCreateButton_click);
        newIssueQuery.find('.feedbackEntryItemCancel').click(feedbackNewIssueOrCommentCancelButton_click);

        newIssueQuery.find('input[type="text"]').keydown(newIssueEntryBox_enterKeyDown);

        newIssueQuery.find('input[type="text"]').twitLimit({
            message: '%1 characters left',
            counterElem: '.charsLeft'
        });

        newIssueQuery.find('.feedbackEntryTextBoxContainer').inputHintOverlay(4, 5);

        return newIssueQuery;
    };

    var getNewIssueHtml = function () {
        return [
            '<div id="feedbackTag_', $axure.utils.createUniqueTag(), '" class="feedbackEntryItem">',
            '   <div class="feedbackEntryTextBoxContainer"><input id="addTopicInput" type="text" title="Add Topic" class="feedbackEntryTextBox" /></div>',
            '   <div class="charsLeft"></div>',
            '   <div class="feedbackEntryItemLinks">',
            (isScreenshotCompatible)
                ?   '       <img class="screenshotIssueIcon" src="' + contentUrlPrefix + '/images/icon-camera.png" onclick="screenshotTakeMainScreenshot(this)" title="' + (isMac ? 'Cmd + Shift + 1' : 'Ctrl + Shift + 1') +  '"/>'
                :   '',
            '       <a class="feedbackEntryItemCancel">clear</a>',
            '       <a class="awesomeButton feedbackEntryItemSave">create</a>',
            '   </div>',
            '</div>'].join("");
    };


    //------------------------------------------------------------------------------------
    // Following 2 functions create the New Comment Entry section inside an Issue
    //------------------------------------------------------------------------------------
    var getNewCommentEntryControl = function (issueCode) {
        // add the area for a new comment entry box
        var newEntryQuery = $(getNewCommentEntryHtml(issueCode));
        // find the submit and cancel links and attach the click events
        newEntryQuery.find('.feedbackEntryItemSave').click(feedbackNewCommentSubmitButton_click);
        newEntryQuery.find('.feedbackEntryItemCancel').click(feedbackNewIssueOrCommentCancelButton_click);

        return newEntryQuery;
    };

    var getNewCommentEntryHtml = function (issueCode) {
        var issue = issueCode ? issueCode : "";
        var cancelButtonText = issueCode ? 'cancel' : 'clear';
        return [
            '<div id="feedbackTag_', $axure.utils.createUniqueTag(), '" issueCode="', issueCode, '" class="feedbackEntryItem">',
            '   <div class="feedbackEntryTextAreaContainer"><textarea rows=5 class="feedbackEntryTextArea" /></div>',
//            '   <div style="float:left;">Commenting as <span class="commenterName">', getCommenterName(), '</span></div>',
            '   <div class="feedbackEntryItemLinks">',
             (!$('#rightPanel').length && !$('#outerContainer').length && !$('#leftPanel').length || !isScreenshotCompatible) 
                ?''
                :'       <img class="screenshotIcon" src="' + contentUrlPrefix + '/images/icon-camera.png" onclick="screenshotTakeMainScreenshot(this)" title="' + (isMac ? 'Cmd + Shift + 1' : 'Ctrl + Shift + 1') + '"/>',
            '       <a class="feedbackEntryItemCancel">', cancelButtonText, '</a>',
            '       <a class="awesomeButton feedbackEntryItemSave">submit</a>',
            '   </div>',
            '</div>'].join("");
    };

    //------------------------------------------------------------------------------------
    // Following 2 functions create the Edit comment entry section that appears when an
    // edit link is clicked for a specific comment
    //------------------------------------------------------------------------------------
    var getEditCommentControl = function (commentId, origCommentText) {
        // add the area for editing an entry
        var editEntryQuery = $(getEditCommentHtml(commentId, origCommentText));
        // find the submit and cancel links and attach the click events
        editEntryQuery.find('.feedbackEntryItemSave').click(editCommentOkLink_click);
        editEntryQuery.find('.feedbackEntryItemCancel').click(editCommentCancelLink_click);

        return editEntryQuery;
    };

    var getEditCommentHtml = function (commentId, origCommentText) {
        return [
            '<div id="feedbackTag_', $axure.utils.createUniqueTag(), '" commentId="', commentId, '" class="feedbackEntryItem">',
            '   <div><textarea rows=5 class="feedbackEntryTextArea">' + origCommentText + '</textarea></div>',
            '   <div class="feedbackEntryItemLinks">',
            (!$('#rightPanel').length && !$('#outerContainer').length && !$('#leftPanel').length || !isScreenshotCompatible) 
                ?''
                :'       <img class="screenshotIcon" src="' + contentUrlPrefix + '/images/icon-camera.png" onclick="screenshotTakeMainScreenshot(this)" title="' + (isMac ? 'Cmd + Shift + 1' : 'Ctrl + Shift + 1') + '"/>',
            '       <a class="feedbackEntryItemCancel">cancel</a>',
            '       <a class="awesomeButton feedbackEntryItemSave">save</a>',
            '</div>',
            '</div>'].join("");
    };

    //------------------------------------------------------------------------------------
    // Following 2 functions create the Delete item prompt that appears when a delete
    // link is clicked for a specific comment or issue
    //------------------------------------------------------------------------------------
    var getDeleteItemControl = function (itemId, itemType) {
        //Generate the html for the delete prompt
        var deleteItemQuery = $(getDeleteItemHtml(itemId, itemType));

        //Find the yes/cancel links and attach the click events
        deleteItemQuery.find('.deleteItemYes').click(deleteItemYesLink_click);
        deleteItemQuery.find('.deleteItemCancel').click(deleteItemCancelLink_click);

        return deleteItemQuery;
    };

    var getDeleteItemHtml = function (itemId, itemType) {
        return [
            '<div id="feedbackTag_', $axure.utils.createUniqueTag(), '" itemid="', itemId, '" class="feedbackDeleteItem">',
            '   <div class="feedbackDeleteConfirm">Are you sure you want to delete this ', itemType, '?</div>',
            '   <div class="feedbackDeleteItemLinks">',
            '       <a class="deleteItemYes">Yes</a>',
            '       <a class="deleteItemCancel">Cancel</a>',
            '   </div>',
            '</div>'].join("");
    };


    //------------------------------------------------------------------------------------
    //----------BEGINNING OF CODE SECTION FOR AJAX RESPONSE HANDLER FUNCTIONS-------------
    //------------------------------------------------------------------------------------
    //Executes after receiving a successful response for an Add or Update comment operation
    var finishAddOrUpdateComment = function (tagVal, data) {
        //If we are no longer on the page that this comment belongs to, just return
        if (data.pageId != $axure.page.id)
            return;

        var newFeedbackDiv = $('#feedbackTag_' + tagVal);
        //var loadingDiv = $('#loading_' + tagVal);
        var existingComment = $('#comment_' + data.comment.id);
        if (existingComment.length > 0) {
            //Existing comment

            var comments = fixCommentDatesAndSort(data.comment);
            var commentQuery = getCommentsControl(comments);

            existingComment.replaceWith(commentQuery);

            // Screenshot Loading Complete Callbacks
            commentQuery.find('.thumbnailImg').each(function () {
                $(this).load(function () {
                    screenshotFinishLoadingCallback($(this).parent().parent());
                    screenshotBindToDiv($(this).parent().parent());
                });
            });

        } else {
            // Hides and inserts here
            // Add Screenshots here somehow

            var issueDivQuery = $('#issue_' + data.comment.issueCode);
            if (issueDivQuery.length > 0) {
                //Add comment to existing issue

                var comments = fixCommentDatesAndSort(data.comment);
                var commentQuery = getCommentsControl(comments);
                newFeedbackDiv.before(commentQuery);

                // Screenshot Loading Complete Callbacks
                commentQuery.find('.thumbnailImg').each(function () {
                    $(this).load(function () {
                        screenshotFinishLoadingCallback($(this).parent().parent());
                        screenshotBindToDiv($(this).parent().parent());
                    });
                });

                issueDivQuery.find('.noCommentsMessage').remove();
                newFeedbackDiv.remove();
            }
        }
    };

    //Executes after a successful response for an Add or Update Issue operation
    var finishAddOrUpdateIssue = function (tagVal, data) {
        //If we are no longer on the page that this issue belongs to, just return
        if (data.pageId != $axure.page.id)
            return;

        var newFeedbackDiv = $('#feedbackTag_' + tagVal);
        var existingIssue = $('#issue_' + data.issue.code);
        if (existingIssue.length > 0 || $('#feedbackSavedIssues').is('.loading')) {
            //Issue update not supported yet!
            newFeedbackDiv.remove();
        } else {
            //Create a new issue
            var issue = fixIssueDatesAndSort(data.issue);
            var issueQuery = getIssuesControl(issue, false); //for now false, to be changed when implementing admin add-issue

            $('#feedbackSavedIssues').prepend(issueQuery);
            $currFeedbackEntry_keyboard = $('#addTopicInput');

            // Displays message to notify users that screenshot functionality is on another browser
            checkIfShowScreenshotBrowserMsg();

            // Screenshot Loading Complete Callbacks
            issueQuery.find('.thumbnailImg').each(function () {
                $(this).load(function () {
                    screenshotFinishLoadingCallback($(this).parent().parent());
                    screenshotBindToDiv($(this).parent().parent());
                });
            });

            newFeedbackDiv.remove();
        }
    };

    //Executes after a successful Delete Comment operation
    var finishDeleteComment = function (tagVal, data) {
        var existingComment = $('#comment_' + data.comment.id);

        if (existingComment.length > 0) {
            existingComment.remove();
        }
    };

    //Executes after a successful Delete Issue operation
    var finishDeleteIssue = function (tagVal, data) {
        var existingIssue = $('#issue_' + data.issue.code);

        if (existingIssue.length > 0) {
            existingIssue.remove();
        }

        if (managePageDeleteCallbackFunc) {
            managePageDeleteCallbackFunc();
            managePageDeleteCallbackFunc = null;
        }

        // Displays message to notify users that screenshot functionality is on another browser
        checkIfShowScreenshotBrowserMsg();
    };

    //Function below keeps querying the server for the Cached Response (max of 10 times) and 
    //triggers one of the above 3 functions 'finish' functions when successful response is received
    var MAX_RETRIES = 10;
    var getResponse = function (tagVal, retryNum, onFinished) {
        var ajaxData = {
            tag: tagVal,
            shortcut: $axure.player.settings.projectId
        };

        //If the authCookieValue is set (a password-protected local prototype), then send the
        //token as well (because cookies don't always get sent to external domains)
        if (authCookieValue) {
            $.extend(ajaxData, { token: authCookieValue });
        }

        $.ajax({
            type: 'GET',
            url: feedbackServiceUrl + '/getResponse',
            data: ajaxData,
            success: function (response) {
                if (response && response.success && response.data) {
                    onFinished(tagVal, response.data);
                } else if (response.success) { // if the response succeeded but we didn't get a comment, then we're waiting
                    if (retryNum < MAX_RETRIES) {
                        setTimeout(function () {
                            getResponse(tagVal, retryNum + 1, onFinished);
                        }, 3000);
                    }
                } else if (!response.success && response.redirecturl) {
                    window.location.href = response.redirecturl;
                }
            },
            dataType: 'jsonp'
        });
    };

    //Executes directly after an ajax post request from one of the click handlers below, 
    //if the return data is retrieved, then execute the 'finish' function or execute 
    //getResponse above which queries for a cached response
    var getResponseOrFinish = function (data, tagVal, onFinished) {
        if (!data) {
            getResponse(tagVal, 0, onFinished);
        } else {
            onFinished(tagVal, data);
        }
    };
    //------------------------------------------------------------------------------------
    //------------END OF CODE SECTION FOR AJAX RESPONSE HANDLER FUNCTIONS-----------------
    //------------------------------------------------------------------------------------

    //Used in comment create/update/delete and issue create/delete
    //Determines if the prototype is hosted and the browser is IE, in which case it sends
    //the request via GET to overcome the cross-domain limitation in IE
    var sendAjax = function (postUrl, postData) {
        //if($.browser.msie && document.URL.toLowerCase().indexOf('http://') != -1) {
        $.ajax({
            type: 'GET',
            url: postUrl,
            data: postData,
            dataType: 'jsonp'
        });
        // 3-28-2012: Got rid of cross-domain post for now so that everything is jsonp for SSL
        //} else {
        //    $.ajax({
        //        type: 'POST',
        //        url: postUrl,
        //        data: postData
        //    });
        //}
    };

    //------------------------------------------------------------------------------------
    // Executes when user click Cancel in either the New Issue or New Comment Entry section
    //------------------------------------------------------------------------------------
    var feedbackNewIssueOrCommentCancelButton_click = function () {
        var parent = $(this).parents('.feedbackEntryItem');
        var newEntries = parent.parents('#feedbackNewIssue');

        var isReply = (newEntries.length == 0);
        if (isReply) {
            parent.parents('.feedbackIssue').find('.editIssueLinks').show();
            parent.remove();
        } else {
            parent.find('input[type=text]').val('');
        }

        // Remove any screenshots attached to New Issue. Comments taken care when whole div is removed.
        newEntries.siblings('.feedbackItem_thumbnail').remove();

        // Removes the screenshot overlay and any related attributes
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;
        screenshotRemoveAll();

        return false;
    };

    //------------------------------------------------------------------------------------
    // Executes when user click Submit in a New Comment Entry section
    //------------------------------------------------------------------------------------
    var feedbackNewCommentSubmitButton_click = function () {
        if (readOnlyMode == true) {
            alert(readOnlyMessage);
            return false;
        }

        if ($('.preloadDiv').length > 0) {
            return false;
        }

        var textArea = $(this).parents('.feedbackEntryItem').find('textarea');
        var inputText = textArea.val();
        var trimmedText = $.trim(inputText);

        if (trimmedText == '' && !textArea.parents('.feedbackEntryItem').find('.feedbackItem_thumbnail').length) {
            textArea.val('');
            return false;
        }

        if ($.browser.msie && document.URL.toLowerCase().indexOf('http://') != -1) {
            var encodedText = encodeURI(trimmedText);
            if (encodedText.length > 1600) {
                alert('Comment text is too long, please split into multiple comments.');
                return false;
            }
        }

        showCommenterNameDialogDiv(completeFeedbackNewCommentSubmitButton_click, $(this));

        return false;
    };
    var completeFeedbackNewCommentSubmitButton_click = function ($thisObj) {
        if ($('.preloadDiv').length) { return; }

        var parent = $thisObj.parents('.feedbackEntryItem');
        var issueCode = parent.attr('issueCode');

        // we want to re-enable the reply link
        parent.parent().find('.editIssueLinks').show();

        var input = parent.find('textarea');

        // Send GUIDS not the data url!
        var submittedImgs = [];
        //parent.parent().find('.feedbackItem_thumbnail').each(function () {
        parent.find('.feedbackItem_thumbnail').each(function () {
            if ($(this).is(':visible')) {
                var idTag = $(this).attr('id');
                submittedImgs.push(idTag.substring(idTag.indexOf('_') + 1));
            }
        });

        var text = $.trim(input.val());
        parent.find('textarea').attr('disabled', 'disabled');
        parent.find('a:first').before('<img src="' + contentUrlPrefix + '/Content/ajaxload.gif" alt="Loading...">');
        parent.find('a').hide();

        // Hide Screenshot button and panel
        parent.find('.screenshotIcon').hide();
        screenshotRemoveAll();

        var tagVal = parent.attr('id').replace('feedbackTag_', '');

        var ajaxData = {
            tag: tagVal,
            issueCode: issueCode,
            pageId: $axure.page.id,
            commenter: getCommenterName(),
            commentText: text,
            shortcut: $axure.player.settings.projectId,
            images: JSON.stringify(submittedImgs),
            success: function (data) {
                getResponseOrFinish(data, tagVal, finishAddOrUpdateComment);
            }
        };

        //If the authCookieValue is set (a password-protected local prototype), then send the
        //token as well (because cookies don't get sent with a POST to external domains)
        if (authCookieValue) {
            $.extend(ajaxData, { token: authCookieValue });
        }

        sendAjax(feedbackServiceUrl + '/addcomment', ajaxData);
    };

    //------------------------------------------------------------------------------------
    // Executes when user click Create in the New Issue Entry section
    //------------------------------------------------------------------------------------
    var feedbackNewIssueCreateButton_click = function () {
        if (readOnlyMode == true) {
            alert(readOnlyMessage);
            return false;
        }

        if ($('.preloadDiv').length) { return false; }

        var input = $(this).parents('.feedbackEntryItem').find('input[type=text]');
        var inputText = input.val();
        if ($.trim(inputText) == '') {
            input.val('');
            input.siblings('label.inputHintOverlay').text('Add Topic (required)');
            return false;
        }

        showCommenterNameDialogDiv(completeFeedbackNewIssueCreateButton_click, $(this));

        return false;
    };

    var completeFeedbackNewIssueCreateButton_click = function ($thisObj) {
        var parent = $thisObj.parents('.feedbackEntryItem');

        var input = parent.find('input[type=text]');

        // Send GUIDS not the data url!
        var submittedImgs = [];
        parent.parent().siblings('.feedbackItem_thumbnail').each(function () {
            if ($(this).is(':visible')) {
                var idTag = $(this).attr('id');
                submittedImgs.push(idTag.substring(idTag.indexOf('_') + 1));
            }
        });

        var text = $.trim(input.val());
        parent.find('input').attr('disabled', 'disabled');
        parent.find('a').attr('disabled', 'disabled').addClass('disabled');
        parent.find('.charsLeft').replaceWith('<img class="topicLoading" src="' + contentUrlPrefix + '/Content/ajaxload.gif" alt="Loading...">');

        var tagVal = parent.attr('id').replace('feedbackTag_', '');

        // Remove all screenshot and preview panels
        screenshotRemoveAll();

        // we want to add a new control for adding a new issue
        var newIssueControl = getNewIssueControl();
        newIssueControl.prependTo('#feedbackNewIssue');

        var ajaxData = {
            tag: tagVal,
            shortcut: $axure.player.settings.projectId,
            pageId: $axure.page.id,
            pageName: $axure.page.pageName,
            creator: getCommenterName(),
            description: text,
            images: JSON.stringify(submittedImgs),
            success: function (data) {
                $('#feedbackMinWidth').children()
                parent.parent().parent().children().nextUntil('.feedbackSavedIssues', '.feedbackItem_thumbnail').each(function () {
                    $(this).remove();
                });

                getResponseOrFinish(data, tagVal, finishAddOrUpdateIssue);
            }
        };

        //If the authCookieValue is set (a password-protected local prototype), then send the
        //token as well (because cookies don't get sent with a POST to external domains)
        if (authCookieValue) {
            $.extend(ajaxData, { token: authCookieValue });
        }

        sendAjax(feedbackServiceUrl + '/add', ajaxData);

        //bring focus to the new issue entry text box that was created above so user
        //can enter next issue quickly
        $('#feedbackNewIssue .feedbackEntryTextBox').focus(function () {
            $currFeedbackEntry_keyboard = $(this);
        });
        $('#feedbackNewIssue .feedbackEntryTextBox').focus();
    };

    //When user hits "enter" key while inside the new issue entry text box, it will
    //be equivalent to hitting the "create" button, i.e. a new issue gets created
    var newIssueEntryBox_enterKeyDown = function (event) {
        if (event.which == '13') {
            if (readOnlyMode == true) {
                alert(readOnlyMessage);
                return false;
            }

            var inputText = $(this).val();
            if ($.trim(inputText) == '') {
                $(this).val('');
                $(this).siblings('label.inputHintOverlay').text('Add Topic (required)');
                return false;
            }
            showCommenterNameDialogDiv(completeFeedbackNewIssueCreateButton_click, $(this));

            return false;
        }
    };

    //------------------------------------------------------------------------------------
    // Executes when user clicks the "Comment" button inside an issue
    //------------------------------------------------------------------------------------
    var issueNewCommentLink_click = function () {
        var thisQuery = $(this);
        var issueParent = thisQuery.parents('div[id^=issue_]');
        var issueCode = issueParent.attr('id').replace('issue_', '');

        var commentEntryControl = getNewCommentEntryControl(issueCode);
        thisQuery.parents('.editIssueLinks')
            .before(commentEntryControl);

        // Text Area focus and event handler
        commentEntryControl.find('.feedbackEntryTextArea').focus(function () {
            $currFeedbackEntry_keyboard = $(this);
        });
        commentEntryControl.find('.feedbackEntryTextArea').focus();

        // hide the comment link
        issueParent.find('.editIssueLinks').hide();

        return false;
    };

    //------------------------------------------------------------------------------------
    // Following 3 functions handle toggling of each Comment's EDIT AND DELETE functionality
    // and Issue's DELETE functionality
    //------------------------------------------------------------------------------------
    var editCommentLink_click = function () {
        var parentItem = $(this).parents('.feedbackItem');
        var commentId = parentItem.attr('id').replace('comment_', '');

        $('.feedbackItem_commentText', parentItem).hide();
        $('.feedbackItem_commentLastModified', parentItem).hide();
        $('.feedbackItem_links', parentItem).hide();

        var commentText = $('.feedbackItem_commentText', parentItem).html().replace(/<br\/*>/ig, '\n').replace(/&nbsp;/ig, ' ');

        var editCommentControl = getEditCommentControl(commentId, commentText);
        $(this).parents('.feedbackItem_links')
            .before(editCommentControl);

        // Text Area focus and event handler
        editCommentControl.find('.feedbackEntryTextArea').focus(function () {
            $currFeedbackEntry_keyboard = $(this);
        });
        editCommentControl.find('.feedbackEntryTextArea').focus();

        // Add Delete Icons and Events to Screenshot Thumbnails
        parentItem.find('.feedbackItem_thumbnail').each(function() {
            bindEditModeToThumbnail($(this));
        });

        return false;
    };

    var deleteCommentLink_click = function () {
        var parentItem = $(this).parents('.feedbackItem');
        var commentId = parentItem.attr('id').replace('comment_', '');

        $(this).parents('.feedbackItem_links')
            .before(getDeleteItemControl(commentId, 'comment'));

        $(this).parents('.feedbackItem_links').hide();

        return false;
    };

    var deleteIssueLink_click = function () {
        var parentIssue = $(this).parents('.feedbackIssue');
        var issueCode = parentIssue.attr('id').replace('issue_', '');

        parentIssue.children('.topicSubHeader')
            .before(getDeleteItemControl(issueCode, 'issue'));

        $(this).hide();

        return false;
    };

    //------------------------------------------------------------------------------------
    // Following 2 functions handle the EDIT COMMENT Save and Cancel links
    //------------------------------------------------------------------------------------
    var editCommentOkLink_click = function () {
        if (readOnlyMode == true) {
            alert(readOnlyMessage);
            return false;
        }

        var textArea = $(this).parents('.feedbackEntryItem').find('textarea');
        var inputText = textArea.val();
        var trimmedText = $.trim(inputText);

        // If there is no text and no new images OR
        // if there is no text and deleted all images(no remaining images via delete during edit)
        if (trimmedText == '' && ((!textArea.parents('.feedbackItem').find('.feedbackItem_thumbnail').length)
            || !textArea.parents('.feedbackItem').find('.feedbackItem_thumbnail').is(':visible'))) {
            textArea.val('');
            return false;
        }

        if ($.browser.msie && document.URL.toLowerCase().indexOf('http://') != -1) {
            var encodedText = encodeURI(trimmedText);
            if (encodedText.length > 1600) {
                alert('Comment text is too long, please split into multiple comments.');
                return false;
            }
        }

        showCommenterNameDialogDiv(completeEditCommentOkLink_click, $(this));

        return false;
    };
    var completeEditCommentOkLink_click = function ($thisObj) {
        if ($('.preloadDiv').length) { return; }

        var parent = $thisObj.parents('.feedbackEntryItem');
        var commentId = parent.attr('commentId');
        var input = parent.find('textarea');

        // Send GUIDS not the data url!
        var submittedImgs = [];
        parent.parent().find('.feedbackItem_thumbnail').each(function () {
            if ($(this).is(':visible')) {
                var idTag = $(this).attr('id');
                submittedImgs.push(idTag.substring(idTag.indexOf('_') + 1));
            }
        });

        var text = $.trim(input.val());
        parent.find('textarea').attr('disabled', 'disabled');
        parent.find('a:first').before('<img src="' + contentUrlPrefix + '/Content/ajaxload.gif" alt="Loading...">');
        parent.find('a').hide();

        // Hide Screenshot button and panel
        parent.find('.screenshotIcon').hide();
        screenshotRemoveAll();

        var tagVal = parent.attr('id').replace('feedbackTag_', '');

        var ajaxData = {
            tag: tagVal,
            pageId: $axure.page.id,
            commentId: commentId,
            commenter: getCommenterName(),
            commentText: text,
            shortcut: $axure.player.settings.projectId,
            images: JSON.stringify(submittedImgs),
            success: function (data) {
                getResponseOrFinish(data, tagVal, finishAddOrUpdateComment);
            }
        };

        //If the authCookieValue is set (a password-protected local prototype), then send the
        //token as well (because cookies don't get sent with a POST to external domains)
        if (authCookieValue) {
            $.extend(ajaxData, { token: authCookieValue });
        }

        sendAjax(feedbackServiceUrl + '/updatecomment', ajaxData);
    };

    var editCommentCancelLink_click = function () {
        var parent = $(this).parents('.feedbackEntryItem');

        // Remove Delete Icons and Unhide Thumbnails Prepared for Deletion
        parent.parent().find('.feedbackItem_thumbnail').each(function() {
            removeEditModeFromThumbnail($(this));
        });

        parent.parents('.feedbackItem').find('.feedbackItem_commentText').show();
        parent.parents('.feedbackItem').find('.feedbackItem_commentLastModified').show();
        parent.parents('.feedbackItem').find('.feedbackItem_links').show();
        parent.remove();

        return false;
    };

    //------------------------------------------------------------------------------------
    // Following 2 functions handle the DELETE ITEM Yes and Cancel links
    //------------------------------------------------------------------------------------
    var deleteItemYesLink_click = function () {
        if (readOnlyMode == true) {
            alert(readOnlyMessage);
            return false;
        }

        var parent = $(this).parents('.feedbackDeleteItem');
        var itemId = parent.attr('itemid');

        var commentDelete = false;
        //If it has a 'feedbackItem' as a parent, it's a comment delete
        if (parent.parents('.feedbackItem').length > 0) {
            commentDelete = true;
        }

        parent.find('a:first').before('<img src="' + contentUrlPrefix + '/Content/ajaxload.gif" alt="Loading...">');
        //parent.find('a').attr('disabled', 'disabled').addClass('disabled');
        parent.find('a').hide();

        var tagVal = parent.attr('id').replace('feedbackTag_', '');

        if (commentDelete) {
            var ajaxData = {
                tag: tagVal,
                commenter: getCommenterName(),
                commentId: itemId,
                shortcut: $axure.player.settings.projectId,
                success: function (data) {
                    getResponseOrFinish(data, tagVal, finishDeleteComment);
                }
            };

            //If the authCookieValue is set (a password-protected local prototype), then send the
            //token as well (because cookies don't get sent with a POST to external domains)
            if (authCookieValue) {
                $.extend(ajaxData, { token: authCookieValue });
            }

            sendAjax(feedbackServiceUrl + '/deletecomment', ajaxData);
        } else {
            var ajaxData = {
                tag: tagVal,
                commenter: getCommenterName(),
                issueCode: itemId,
                shortcut: $axure.player.settings.projectId,
                success: function (data) {
                    getResponseOrFinish(data, tagVal, finishDeleteIssue);
                }
            };

            //If the authCookieValue is set (a password-protected local prototype), then send the
            //token as well (because cookies don't get sent with a POST to external domains)
            if (authCookieValue) {
                $.extend(ajaxData, { token: authCookieValue });
            }

            sendAjax(feedbackServiceUrl + '/delete', ajaxData);
        }

        return false;
    };

    var deleteItemCancelLink_click = function () {
        var parent = $(this).parents('.feedbackDeleteItem');

        //If it has a 'feedbackItem' as a parent, it's within a comment, so show the edit/delete links again
        if (parent.parents('.feedbackItem').length > 0) {
            parent.parents('.feedbackItem').find('.feedbackItem_links').show();
        } else if (parent.parents('.feedbackIssue').length > 0) {
            parent.parents('.feedbackIssue').find('.deleteIssueLink').show();
        }

        parent.remove();

        return false;
    };
})();

//------------------------------------------------------------------------------------
// Screenshot Management
//------------------------------------------------------------------------------------
var isChrome = window.chrome != null && window.navigator.vendor === "Google Inc.";
var isFirefox = typeof InstallTrigger !== 'undefined';
var isIE8 = document.documentMode == 8 && window.msPerformance == null;
var isIE9 = document.documentMode == 9 && window.msPerformance != null;
var isIE10 = document.documentMode == 10;
var isScreenshotCompatible = isChrome || isFirefox;
var isWindows = navigator.appVersion.indexOf("Win") != -1;
var isMac = navigator.appVersion.indexOf("Mac") != -1;
var isScreenshotSetup = true;
var isRequestingScreenshot = false;
var isExtensionAttached = false;
var cropTool;

var tempCount = 0;      // Number of screenshots taken (temporary id)
var $currFeedbackEntry; // Current element (Issue/Comment) user is on (used for attaching the screenshot on mouse)
var $currFeedbackEntry_keyboard; // Current element (Issue/Comment user is on (used for attaching the screenshot via keyboard)

// Bug (resolved): Screwy IE8 Issue. 'window' by itself will cause issues as it doesn't know which 'window' we are referring to.
if (isScreenshotCompatible) {
    window.addEventListener('message', function (event) {
        var isFileUrl = window.location.origin === "file://" || window.location.origin == "null";

        if ((event.origin === window.location.origin && !isFileUrl) || isFileUrl) {
            // Listen to screenshot results
            if (isRequestingScreenshot
                    && typeof event.data === 'object' && event.data !== null
                    && event.data.dataUrl) {
                isRequestingScreenshot = false;
                screenshotAppendMainScreenshot(event.data.dataUrl);
            }

            // Listens to Keyboard request to take screenshot
            if (event.data === 'youShouldTakeAScreenshot' &&
                isExtensionAttached &&
                $('#feedbackContainer').is(':visible')) {

                // Restore default if values were hidden
                if (!$currFeedbackEntry_keyboard.is(':visible')) {
                    $currFeedbackEntry_keyboard = $('#addTopicInput');
                }

                //screenshotTakeMainScreenshot($currFeedbackEntry_keyboard.parents('.feedbackEntryItem').find('.screenshotIssueIcon, .screenshotIcon'));
                $currFeedbackEntry_keyboard.parents('.feedbackEntryItem').find('.screenshotIssueIcon, .screenshotIcon').click();
            }

            // Respond to attach extension
            if (/*!isExtensionAttached &&*/
                event.data === 'isAxure' &&
                $('#feedbackContainer').length) {

                // Legacy: Should work since the extension is the one asking if the page is axure. aka the extension already exists
                isExtensionAttached = true;
                isScreenshotSetup = false;

                if (isFileUrl) {
                    window.postMessage('yesIAmAxure', '*');
                } else {
                    window.postMessage('yesIAmAxure', window.location.origin);
                }
            }

            // Response to "yesIAmAxure" confirms that both the extension exists and the page was able to communicate with the extension
            // Mainly used for local files aka fileURLS since they work a bit different from regular pages
            if (event.data === "okContinueAxure") {
                isExtensionAttached = true;
                isScreenshotSetup = false;
            }

            // Firefox: Extension remembers, but doesn't notify web page
            if (!isExtensionAttached &&
                event.data === 'youAreAxure!') {
                isExtensionAttached = true;
                isScreenshotSetup = false;
            }
        }
        event.stopPropagation();
        event.preventDefault();
    }, false);

    window.addEventListener('scroll', function(event) {
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;
        screenshotRemoveAll();
    }, false);

    window.addEventListener('resize', function(event) {
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;
        screenshotRemoveAll();
    }, false);
}

$(document).keyup(function(e) {
    if (e.keyCode == 27) {      // esc key
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;
        screenshotRemoveAll();
    }
});

// Set state when page has finished loading 
// And removes the screenshot/preview panel if page is resized
$(window).load(function () {
    window.loaded = true;
    $currFeedbackEntry_keyboard = $('#addTopicInput');

    // Listener: Removes screenshot/preview panel if present
    $('.vsplitbar').mousedown(function () {
        if ($('#screenshotPanel').length || $('#previewPanel').length) {
            screenshotIconSpinnerShow(false);
            isRequestingScreenshot = false;
            screenshotRemoveAll();
        }
    });

    $("a[pluginid='sitemapHost'], a[pluginid='pageNotesHost'], #interfaceControlFrameMinimizeButton").click(function () {
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;
        screenshotRemoveAll();
    });

    $('#interfaceControlFrameMinimizeButton').click(function () {
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;
        screenshotRemoveAll();
    });

    //showIssueOrComment();
});

// does feedback plugin redirection and scrolling to respective
// issue/comment dependent on fragment variables
var showIssueOrComment = function () {
    var type = getFragmentVariable('type');
    //var show = getFragmentVariable("show");

    if (type == false /*|| show != false*/) { return; }

    var p = getFragmentVariable("p");

    var newIssue = getFragmentVariable("newIssueText");
    var newIssueText = newIssue == false ? "" : decodeURIComponent(newIssue);

    var newCommentId = getFragmentVariable("newCommentId");
    var newComment = getFragmentVariable("newCommentText");
    var newCommentText = newComment == false ? "" : decodeURIComponent(newComment);

    var issueCode = getFragmentVariable("issueCode");
    var commentId = getFragmentVariable("commentId");

    // Show feedback tab
    $("#interfaceControlFrameHeader [pluginid='feedbackContainer']").click();

    // Jump back to adding a new issue (Coming from after installing extension)
    if (newIssueText != "") {
        (function waitforIssue() {
            var $newIssue = $('#addTopicInput');
            if (window.loaded && $newIssue.length) {
                $newIssue.val(newIssueText);
                $newIssue.focus();
            } else {
                setTimeout(waitforIssue, 1000);
            }
        })();
    }

    // Scroll to Issue and Comment
    if (issueCode != false) {
        (function issueLoad() {
            var $issue = $('#issue_' + issueCode);
            if ($issue.length) {
                var $commentBlock = $issue.find('.issueComments');

                // Issue scrollTo
                $issue.get(0).scrollIntoView();

                if (type == "comment") {
                    $issue.find('.expandIssueLink').click();

                    // Viewing a specific comment
                    if (commentId) {
                        (function wait() {
                            var $comment = $('#comment_' + commentId);
                            if ($commentBlock.children().size() > 1 || $comment.length) {
                                if ($comment.length) {
                                    $comment.get(0).scrollIntoView();
                                }
                            } else {
                                setTimeout(wait, 500);
                            }
                        })();
                    }

                    // Jump back to adding a new comment into an exisiting issue (Coming back from instaling extension)
                    if (newCommentId) {
                        (function waitForAdd() {
                            var $addCommentButton = $issue.find('.feedbackIssueNewCommentLink');
                            if ($addCommentButton.length) {
                                $addCommentButton.click();

                                // Add comment text if there was one originally
                                if (newCommentText != "") {
                                    $issue.find('.feedbackEntryTextArea').val(newCommentText);
                                }
                            } else {
                                setTimeout(waitForAdd, 500);
                            }
                        })();
                    }
                }
            } else {
                setTimeout(issueLoad, 1000);
            }
        })();
    }

    // Can't change url w/out refreshing page so just trick the url
    // so when there is a refresh, the page won't jump to the discussions
    //window.location.hash += "&show=false";
    window.location.hash = "p=" + p;
}

var checkIfShowScreenshotBrowserMsg = function () {
    //(!$('#rightPanel').length && !$('#outerContainer').length && !$('#leftPanel').length || !isScreenshotCompatible) 
    if (isScreenshotCompatible || !$('#rightPanel')) { return; }

    var $target = $('#feedbackSavedIssues');

    // Create if empty
    if ($target.html() == "" && !$('#browserMsg').length) {
        var browserMsg = document.createElement('div');
        browserMsg.setAttribute('id', 'browserMsg');
        browserMsg.innerHTML = "To capture images, please use Google Chrome or Mozilla Firefox.";

        $target.append(browserMsg);
    } else {
        // Else Delete
        $('#browserMsg').remove();
    }
};

var getFragmentVariable = function(variable) {
    var query = window.location.hash.substring(1);
    var vars = query.split("&");
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split("=");
        if (pair[0] == variable) { return pair[1]; }
    }
    return false;
}

var screenshotIconSpinnerShow = function(bool) {
    if (bool) {
        $('.screenshotIcon').parent().each(function() {
            var loading = document.createElement('img');
            loading.setAttribute('class', 'screenshotGenerating');
            loading.setAttribute('src', contentUrlPrefix + '/Content/ajaxload.gif');

            $(this).append(loading);
        });

        $('.screenshotIssueIcon').parent().each(function() {
            var loading = document.createElement('img');
            loading.setAttribute('class', 'screenshotGenerating');
            loading.setAttribute('src', contentUrlPrefix + '/Content/ajaxload.gif');

            $(this).append(loading);
        });
    } else {
        $('.screenshotGenerating').remove();
    }
};

var screenshotCheckPlugin = function (source) {
    if (!isExtensionAttached) {
        screenshotIconSpinnerShow(false);
        isRequestingScreenshot = false;

        // Missing Extension Dialog
        if (!$('#missingExtensionDialog').length) {
            var dialog = document.createElement('div');
            dialog.setAttribute('id', 'missingExtensionDialog');
            dialog.setAttribute('class', 'dialogDiv');

            $('#feedbackMinWidth').append(dialog);

            $('#missingExtensionDialog').axDialog();

            if (isScreenshotCompatible) {
                var chromeLink = '\'https:\/\/chrome.google.com\/webstore\/detail\/axure-share-extension-for\/fiidicpfcmmoobdpadllfjklbdegfjaf?hl=en\'';
                var ffLink = '\'https:\/\/addons.mozilla.org\/en-US\/firefox\/addon\/axure-share-add-on-for-firefox\/\'';
                $('#missingExtensionDialog').html(
                    'Please install the following extension to capture images.<br/><br/>' +
                    '<input id="linkButton" type="button" value="Download Extension" onclick="window.open(' +
                    (isChrome ? chromeLink : ffLink) +
                    ')"><br/><br/>' +
                    'After installing, <a id="reloadLink" href="#">reload this page</a>.');

                $('#reloadLink').click(function () {
                    // Get Issue text
                    var newIssueText = $('#addTopicInput').val();

                    // Get Comment's Issue code
                    var newCommentIssueCode = $(source).parents('.feedbackEntryItem').attr('issuecode');

                    // Get Comment Id
                    var newCommentId = $(source).parents('.feedbackEntryItem').attr('id');

                    // Get Comment Text
                    var newCommentText = $(source).parents('.feedbackEntryItem').find('.feedbackEntryTextArea').val();

                    // Checks if screenshot icon was clicked from a comment
                    var isNewComment = $(source).attr('class') != 'screenshotIssueIcon';

                    window.location.hash
                        += (newIssueText != "" ? "&newIssueText=" + encodeURIComponent(newIssueText) : "")
                        + (isNewComment && newCommentIssueCode.length && newCommentId.length
                            ? "&type=comment&issueCode=" + newCommentIssueCode + "&newCommentId=" + newCommentId
                            : "&type=issue")
                        + (newCommentText != "" ? "&newCommentText=" + encodeURIComponent(newCommentText) : "");

                    location.reload(true);
                });
            }
        }

        if (isScreenshotCompatible) {
            $('#missingExtensionDialog').axDialog('show');

            // Re-locate dialog box and overlay
            setTimeout(function () {
                $('#missingExtensionDialogOverlay').css('width', $('#feedbackContainer').width() + 'px');
                $('#missingExtensionDialogOverlay').css('height', ($('#feedbackContainer').height() - 20) + 'px');
                $('#missingExtensionDialog').css('top', parseInt($('#missingExtensionDialog').css('top')) / 2 + 'px');

                var left = ($('#missingExtensionDialogOverlay').width() - $('#missingExtensionDialog').width()) * .417;
                $('#missingExtensionDialog').css('left', left + 'px');
            }, 50);
        }

        return false;
    }

    return true;
};

var screenshotTakeMainScreenshot = function (elem) {
    if (!window.loaded) { return; }

    // Chrome Bug: If screenshot icon is hit when there is already a screenshot/preview up, it takes a picture
    // of the overlay as well! Just prevent the repeated click.
    if ($('.blackoutOverlay, .blackoutPreviewOverlay').is(':visible')) { return; }

    if (isRequestingScreenshot) { return; }

    isRequestingScreenshot = true;

    screenshotIconSpinnerShow(true);

    if (isScreenshotSetup) {
        if (!screenshotCheckPlugin(elem)) {
            return;
        }
    }

    // Cleanup Any Old Screenshot
    screenshotRemoveAll();

    // Identify which issue/comment the screenshot is being taken for
    $currFeedbackEntry = $(elem).parent().parent();

    // Request for screenshot to be taken from plugin
    screenshotRequestPluginScreenshot();
};

var screenshotRequestPluginScreenshot = function () {
    if (isFirefox || isChrome) {
        if (window.location.origin === "file://" || window.location.origin == "null") {
            window.postMessage('axTakeScreenshot', '*');
        } else {
            window.postMessage('axTakeScreenshot', window.location.origin);
        }
    }
}

var screenshotAppendMainScreenshot = function (dataUrl) {
    //console.log("Recieved Screenshot!");

    // rightPanel dimensions
    var zIndex = parseInt($('#rightPanel').css('z-index')); 
    var x = parseInt($('#rightPanel').css('left'));
    var width = parseInt($('#rightPanel').css('width'));
    var height = parseInt($('#rightPanel').css('height'));

    // Crop and resize screenshot
    var tempCanvas = document.createElement('canvas');
    tempCanvas.setAttribute('class', 'tempCanvas');
    tempCanvas.setAttribute('width', width);
    tempCanvas.setAttribute('height', height);
    var tempContext = tempCanvas.getContext('2d');
    var tempImage = new Image();

    // Adds screenshot overlay container
    screenshotAddPanel();

    tempImage.onload = function() {
        // Crop image to obtain only right panel
        tempContext.drawImage(this, x, 0, width, height, 0, 0, width, height);

        // Append screenshot over rightPanel of page
        var screenshot = document.createElement('img');
        screenshot.setAttribute('id', 'screenshotImg');
        screenshot.setAttribute('class', 'screenshotOverlay');
        screenshot.setAttribute('src', tempCanvas.toDataURL());
        screenshot.setAttribute('width', width);
        screenshot.setAttribute('height', height);
        screenshot.style.zIndex = zIndex + 1;
        screenshot.style.left = x.toString() + 'px';
        $('#screenshotPanel').append(screenshot);

        // Append crop tool
        screenshotAddCropTool($('#screenshotImg'));

        // TODO: Add listener to container for resizing?

        // Adds screenshot overlay img
        screenshotAddOverlay(screenshot.style.zIndex + 1);
        screenshotOverlayVisible(true);

        // Workaround for temporary cleaning up 
        $('.axScreenshotCanvas').remove();

        // Stop Spinner
        screenshotIconSpinnerShow(false);
    }
    tempImage.src = dataUrl;
}

var screenshotAddCropTool = function($img) {
    cropTool = $img.axImgAreaSelect({
        instance: true,
        handles: true,
        onOuterOverlayShown: function() { screenshotOverlayVisible(false); },
        onSelectEnd: function () {
            screenshotDoCrop();
            screenshotRemoveAll();
        },
        onSelectCancel: function () { screenshotRemoveAll(); }
    });
};

var screenshotDoCrop = function() {
    var bounds = cropTool.getSelection();

    if (bounds.height <= 0 || bounds.width <= 0) return;

    var baseImg = document.getElementById('screenshotImg');

    var cropCanvas = document.createElement('canvas');
    cropCanvas.setAttribute('class', 'tempCropCanvas');
    cropCanvas.setAttribute('hidden', true);
    cropCanvas.setAttribute('width', bounds.width);
    cropCanvas.setAttribute('height', bounds.height);

    var cropContext = cropCanvas.getContext("2d");

    try {
        cropContext.drawImage(baseImg, bounds.x1, bounds.y1, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height);

        var resultUrl = cropCanvas.toDataURL("image/png", 1.0);
        var feedbackServiceUrl = (window.AXSHARE_HOST_SECURE_URL || 'https://share.axure.com') + '/issue';
        var tempId = tempCount++;

        // General Screenshot Container
        var overallDiv = document.createElement('div');
        overallDiv.setAttribute('class', 'feedbackItem_thumbnail');
        overallDiv.setAttribute('id', 'screenshot_' + tempId);

        // Image Container
        var imgDiv = document.createElement('div');
        imgDiv.setAttribute('class', 'feedbackItem_thumbnailImg');

        // Temporary Thumbnail Image
        var image = document.createElement('img');
        image.setAttribute('src', resultUrl);       // Sets img url here
        image.style.maxWidth = '90%';
        image.style.maxHeight = '120px';
        image.style.display = 'block';
        image.style.margin = '0 auto';

        // Fullsize Image (Hidden, just use to store src for preview)
        var fsImage = document.createElement('img');
        fsImage.setAttribute('class', 'fullsizeImg');
        fsImage.setAttribute('src', resultUrl);

        imgDiv.appendChild(image);
        imgDiv.appendChild(fsImage);
        overallDiv.appendChild(imgDiv);

        // Loading State Container
        var loadingDiv = document.createElement('div');
        loadingDiv.setAttribute('class', 'preloadDiv');

        overallDiv.appendChild(loadingDiv);

        // Appends thumbnail to the correct issue comment
        var feedbackType = "";
        if ($currFeedbackEntry.parent().attr('id') === 'feedbackNewIssue') {
            // New Issue
            $('#feedbackNewIssue').after(overallDiv);
            feedbackType = "issue";

            // Adjust div width
            $(overallDiv).css('margin-left', '10px');
            $(overallDiv).css('margin-right', '10px');
        } else if ($currFeedbackEntry.attr('id').indexOf('feedbackTag_') > -1) {
            // New Comment/Edit Comment
            $currFeedbackEntry.append(overallDiv);
            feedbackType = "comment"; 
        }

        // Scrolls page down to newly added screenshot
        $(overallDiv).get(0).scrollIntoView();

        // Focuses on text element after crop has been made
        var parent = $(overallDiv).parent();
        if (parent.attr('id') == 'feedbackMinWidth') {
            parent.find('#addTopicInput').focus();
        } else {
            parent.find('.feedbackEntryTextArea').focus();
        }

        // Thumbnail Generation
        var fullsizeImg = new Image();
        var thumbnailCanvas = document.createElement('canvas');
        var thumbnailContext = thumbnailCanvas.getContext('2d');

        image.onload = function () {
            var height = $(image).height();
            var width = $(image).width();

            thumbnailCanvas.height = height;
            thumbnailCanvas.width = width;

            fullsizeImg.src = resultUrl;
        };

        fullsizeImg.onload = function () {
            thumbnailContext.drawImage(fullsizeImg, 0, 0, fullsizeImg.width, fullsizeImg.height,
                0, 0, thumbnailCanvas.width, thumbnailCanvas.height);

            var thumbnailUrl = thumbnailCanvas.toDataURL("image/png", 1.0);

            image.setAttribute('class', 'thumbnailImg');
            image.style.maxWidth = '';
            image.style.maxHeight = '';
            image.style.display = '';
            image.style.margin = '';

            var top = ($(imgDiv).height() - thumbnailCanvas.height) / 2;
            image.style.top = top + 'px';

            var ajaxData = {
                image: resultUrl,
                thumbnail: thumbnailUrl,
                type: feedbackType
            };

            //If the authCookieValue is set (a password-protected local prototype), then send the
            //token as well (because cookies don't get sent with a POST to external domains)
            if (authCookieValue) {
                $.extend(ajaxData, { token: authCookieValue });
            }

            // Preuploading Images
            $.ajax({
                type: 'POST',
                url: feedbackServiceUrl + '/imagepreupload',
                data: ajaxData,
                success: function (data) {
                    if (data.success) {
                        screenshotCompletePreload(tempId, data.id);
                        screenshotBindToDiv($(overallDiv));
                        bindEditModeToThumbnail($(overallDiv));
                    } else {
                        $(overallDiv).remove();
                    }
                },
                dataType: 'json'
            });
        };
    } catch (e) {
        screenshotOverlayVisible(true);
    }
};

// Attaches guid to thumbnails and removes the loading img 
var screenshotCompletePreload = function(tempId, newId) {
    $('#screenshot_' + tempId).attr('id', 'screenshot_' + newId);
    $('#screenshot_' + newId).find('.preloadDiv').remove();
    //$('#screenshot_' + newId).css("height", "");
};

// Add Screenshot container over the actual visible prototype
var screenshotAddPanel = function() {
    if (!$('#screenshotPanel').length) {
        // rightPanel dimensions
        var zIndex = parseInt($('#rightPanel').css('z-index'));
        var x = parseInt($('#rightPanel').css('left'));
        var width = parseInt($('#rightPanel').css('width'));
        var height = parseInt($('#rightPanel').css('height'));

        // Build screenshot container
        var screenshotPanel = document.createElement('div');
        screenshotPanel.setAttribute('id', 'screenshotPanel');
        screenshotPanel.style.zIndex = zIndex + 1;
        screenshotPanel.style.left = x.toString() + 'px';
        screenshotPanel.style.width = width.toString() + 'px';
        screenshotPanel.style.height = height.toString() + 'px';
        $('#outerContainer').append(screenshotPanel);
    }
};

// Semi-Transparent Overlay 
var screenshotAddOverlay = function (zIndex) {
    if (!$('#screenshotOverlay').length) {
        // rightPanel dimensions
        var x = parseInt($('#rightPanel').css('left'));
        var width = parseInt($('#rightPanel').css('width'));
        var height = parseInt($('#rightPanel').css('height'));

        // Build overlay image
        var overlayImg = document.createElement('img');
        overlayImg.setAttribute('id', 'screenshotOverlay');
        overlayImg.setAttribute('class', 'blackoutOverlay');
        overlayImg.setAttribute('width', width);
        overlayImg.setAttribute('height', height);
        overlayImg.style.zIndex = zIndex;
        overlayImg.style.left = x.toString() + 'px';

        $('#screenshotPanel').append(overlayImg);

        // Build Instructional Div
        var overlayDiv = document.createElement('div');
        overlayDiv.innerHTML = "Click and drag to<br/>capture an image";
        overlayDiv.setAttribute('class', 'blackoutTextOverlay');
        overlayDiv.style.zIndex = zIndex + 1;

        $('#screenshotPanel').append(overlayDiv);
        $('#screenshotPanel').keypress(function(event) {
            if (event.which == 27) {
                screenshotRemoveAll();
            }
        });

        overlayDiv.style.left = (x + (width / 2) - ($(overlayDiv).width() / 2)).toString() + 'px';
        overlayDiv.style.top = (height / 2).toString() + 'px';
    }
}

// Trigger the Semi-Transparent Overlay on or off
var screenshotOverlayVisible = function(bool) {
    if ($('.blackoutOverlay')) {
        if (bool) {
            $('.blackoutOverlay').show();
            $('.blackoutTextOverlay').show();
        } else {
            $('.blackoutOverlay').hide();
            $('.blackoutTextOverlay').hide();
        }
    }
}

var screenshotRemoveAll = function() {
    if (cropTool) {
        cropTool.remove();
    }

    $currFeedbackEntry = null;

    $('.blackoutOverlay').remove();
    $('#screenshotImg').remove();
    $('#screenshotPanel').remove();
    $('#previewPanel').remove();
};

// Function used in callback to coreectly position the image
var screenshotFinishLoadingCallback = function ($overallDiv) {
    var $img = $overallDiv.find('.thumbnailImg');

    (function wait() {
        if ($img.is(':visible')) {
            // Image Alignment
            //var top = ($overallDiv.height() - $img.height()) / 2;
            //$img.css('top', top + 'px');

            // Remove Loading Img Div
            $overallDiv.find('.preloadDiv').remove();

            // Image Div Adjustment: Resize div to fit image
            //$overallDiv.find('.feedbackItem_thumbnailImg').css("height", "100%");
        } else {
            setTimeout(wait, 1000);
        }
    })();
};

//-----------------------------------------------------------------------------
// Preview Management
//-----------------------------------------------------------------------------
var previewAddPanel = function() {
    if (!$('#previewPanel').length) {
        var zIndex, x, y, width, height, $parent;

        if ($('#rightPanel').length) {
            // rightPanel dimensions
            zIndex = parseInt($('#rightPanel').css('z-index'));
            x = parseInt($('#rightPanel').css('left'));
            width = parseInt($('#rightPanel').css('width'));
            height = parseInt($('#rightPanel').css('height'));

            // Assign parent to connect to
            $parent = $('#outerContainer');
        } else {
            // Document dimensions
            zIndex = 200;
            x = 0;
            y = 0;
            width = $(window).width();
            height = $(window).height();

            // Assign parent to connect to
            $parent = $(document.body);
        }

        // Build screenshot container
        var previewPanel = document.createElement('div');
        previewPanel.setAttribute('id', 'previewPanel');
        previewPanel.style.zIndex = zIndex + 1;
        previewPanel.style.left = x.toString() + 'px';
        previewPanel.style.width = width.toString() + 'px';
        previewPanel.style.height = height.toString() + 'px';

        if (y != null) {
            previewPanel.style.top = y.toString() + 'px';
        }

        $parent.append(previewPanel);
    }
};

// Semi-Transparent Overlay 
var previewAddOverlay = function(zIndex) {
    if (!$('#previewOverlay').length) {
        var width, height;

        if ($('#rightPanel').length) {
            // rightPanel dimensions
            width = parseInt($('#rightPanel').css('width'));
            height = parseInt($('#rightPanel').css('height'));
        } else {
            // Document dimensions
            width = $(window).width();
            height = $(window).height();
        }

        // Build overlay image
        var overlayImg = document.createElement('img');
        overlayImg.setAttribute('id', 'previewOverlay');
        overlayImg.setAttribute('class', 'blackoutPreviewOverlay');
        overlayImg.setAttribute('width', width);
        overlayImg.setAttribute('height', height);
        overlayImg.style.zIndex = zIndex;
        $('#previewPanel').append(overlayImg);
    }
};

var previewAddImg = function() {
    if (!$('.previewImg').length) {
        var previewImg = document.createElement('img');
        previewImg.setAttribute('class', 'previewImg');
        previewImg.style.zIndex = parseInt($('#previewOverlay').css('z-index')) + 1;

        $('#previewPanel').append(previewImg);
    }
};

// Binds ability to preview the thumbnail
var screenshotBindToDiv = function ($currentScreenshotDiv) {
    var id = $currentScreenshotDiv.attr('id');

    // Thumbnail Expand (Preview)
    $currentScreenshotDiv.find('.thumbnailImg').click(function() {
        screenshotRemoveAll();

        if (!$('#previewPanel').length) {
            previewAddPanel();
        }

        var $previewPanel = $('#previewPanel');
        if (!$('.blackoutOverlay').length) {
            previewAddOverlay(parseInt($previewPanel.css('z-index')) + 1);
        }
        if (!$('.previewImg').length) {
            previewAddImg();

            var loading = document.createElement('div');
            loading.setAttribute('class', 'preloadDiv');
            loading.style.zIndex = 100;

            $('#previewPanel').append(loading);
        }

        var $previewImg = $('.previewImg');
        $previewImg.attr('id', id);

        $previewImg.load(function() {
            var containerHeight = parseInt($('#previewPanel').css('height'));
            var containerWidth = parseInt($('#previewPanel').css('width'));
            var imgHeight = parseInt($previewImg.css('height'));
            var imgWidth = parseInt($previewImg.css('width'));
            var top = (containerHeight - imgHeight) / 2;
            var left = (containerWidth - imgWidth) / 2;

            // Image Centering
            $previewImg.css('top', top + 'px');
            $previewImg.css('left', left + 'px');

            // Close Icon
            $('.previewCloseIcon').remove();
            var closeIcon = document.createElement('img');
            closeIcon.setAttribute('class', 'previewCloseIcon');
            //closeIcon.setAttribute('src', '../../images/closeIcon.png');
            closeIcon.setAttribute('src', contentUrlPrefix + '/images/icon_delete.png');
            $(closeIcon).css('top', (top - 18) + 'px');
            $(closeIcon).css('left', (left + imgWidth - 18) + 'px');
            $(closeIcon).css('z-index', parseInt($previewImg.css('z-index')) + 1);
            $previewPanel.append(closeIcon);

            $('#previewPanel').find('.preloadDiv').remove();

            // Remove all when icon is clicked
            $(closeIcon).click(function() {
                $previewPanel.remove();
            });

            // Remove all when panel, tabs, minimize/close, or refresh is hit
            $('#previewPanel, #interfaceControlFrameHeader, ' +
                '#interfaceControlFrameMinimizeContainer, #refreshFeedbackLink').click(function () {
                    $previewPanel.remove();
                });
        });

        $previewImg.attr('src', $currentScreenshotDiv.find('.fullsizeImg').attr('src'));
    });
};

// Adds and binds the delete icon for the screenshot
var bindEditModeToThumbnail = function($div) {
    // Delete Link Container
    var deleteDiv = document.createElement('div');
    deleteDiv.setAttribute('class', 'feedbackItem_deleteDiv');

    var icon = document.createElement('a');
    icon.setAttribute('class', 'deleteScreenshotLink');
    icon.innerHTML = 'x';
    icon.setAttribute('href', '#');

    deleteDiv.appendChild(icon);
    $div.append(deleteDiv);

    // Delete Icon Alignment
    var $iconDiv = $div.find('.feedbackItem_deleteDiv');
    $iconDiv.css('top', '0px');

    // Div Size Adjustment
    var $img = $div.find('.thumbnailImg');
    $img.parent().css('padding-top', '10px');
    $img.parent().css('padding-bottom', '10px');

    // Delete Icon Action
    $iconDiv.click(function () {
        $div.hide();
    });
};

// Remove the screenshot delete links and readjust screenshot
var removeEditModeFromThumbnail = function($div) {
    $div.show();
    $div.find('.feedbackItem_deleteDiv').remove();
    $div.find('.feedbackItem_thumbnailImg').css('padding-top', '0px');
    $div.find('.feedbackItem_thumbnailImg').css('padding-bottom', '0px');
};